
import os
import wx
import wx.lib.colourselect as csel
import wx.lib.customtreectrl as ct

import ab_ids as ai
import ab_config as ac
import ab_plugins as ap
import ab_utils as utils


class PreferencesDialog(wx.Dialog):
    def __init__(self, parent, app):
        wx.Dialog.__init__(
            self, parent, -1, "Preferences",
            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)

        self.app = app
        self.parent = parent

        v_sizer = self.initControls()

        self.SetSizer(v_sizer)
        self.Layout()
        self.Fit()

    def initControls(self):
        PAGES = (
            {'name': 'Display Options',
             'page': DisplayPage,
             'subpages': (
                 {'name': 'Colors', 'page': ColorPage},
                 {'name': 'Tree', 'page': TreePage}
             )},
            {'name': 'Plug-in Settings',
             'page': PlxPage,
             'subpages': (
                 {'name': 'Enabler', 'page': PlxEnablePage},
                 {'name': 'Extra Dirs', 'page': PlxDirsPage},
                 {'name': 'Configuration', 'page': PlxConfigPage}
             )},
            {'name': 'Developer Options', 'page': DebugPage}
        )

        def _addSubpage(pages, treebook, parent, subpages):
            for sb in subpages:
                name = sb.get('name')
                page = sb.get('page')(parent or treebook, self.app, name)
                if page.isValid():
                    pages.append(page)

                    if parent is None:
                        treebook.AddPage(page, name)
                    else:
                        treebook.AddSubPage(page, name)

                    if 'subpages' in sb:
                        _addSubpage(pages, treebook,
                                    treebook, sb.get('subpages'))
                else:
                    page.Destroy()

        v_sizer = wx.FlexGridSizer(2, 0, 1, 0)
        v_sizer.AddGrowableCol(0)
        v_sizer.AddGrowableRow(0)

        treebook = wx.Treebook(self, -1, style=wx.BK_DEFAULT)
        v_sizer.Add(treebook, 1, wx.EXPAND | wx.ALL, 5)

        self.pages = list()
        _addSubpage(self.pages, treebook, None, PAGES)

        v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                    0, wx.ALL | wx.ALIGN_RIGHT, 3)

        self.Bind(wx.EVT_BUTTON, self.OnOk)
        return v_sizer

    def OnOk(self, event):
        id = event.GetEventObject().GetId()
        if id == wx.ID_OK:
            for page in self.pages:
                if hasattr(page, 'validate') and not page.validate():
                    return

        self.EndModal(id)

    def save(self):
        for page in self.pages:
            page.save()


class PagePanel(wx.Panel):
    def __init__(self, parent, app, title):
        wx.Panel.__init__(self, parent, -1, style=wx.NO_BORDER)

        self.app = app
        self.parent = parent

        v_sizer = wx.BoxSizer(wx.VERTICAL)
        if self.isValid():
            v_sizer.Add(self.initControls(), 1, wx.EXPAND | wx.ALL)

        self.SetSizer(v_sizer)
        self.SetAutoLayout(True)
        v_sizer.Fit(self)
        self.Layout()

    def initControls(self):
        raise Exception('not initialized panel')

    def isValid(self):
        return True


class DisplayPage(PagePanel):
    def initControls(self):
        v_sizer = wx.BoxSizer(wx.VERTICAL)

        sizer_line = wx.StaticBoxSizer(wx.StaticBox(self, -1), wx.VERTICAL)
        val = self.app.readKey(ai.CfgParaEnableTextMode)
        self.checkbox_text = wx.CheckBox(
            self, -1, 'Enable Text Mode for plain-text file')
        self.checkbox_text.SetValue(val)
        sizer_line.Add(self.checkbox_text, 0, wx.ALL, 2)

        val = self.app.readKey(ai.CfgParaEnableAutoLine)
        self.checkbox_resizing = wx.CheckBox(
            self, -1, 'Recalculate the number per line when resizing')
        self.checkbox_resizing.SetValue(val)
        sizer_line.Add(self.checkbox_resizing, 0, wx.ALL, 2)

        val = self.app.readKey(ai.CfgParaLineLength)
        self.text_display = wx.StaticText(
            self, -1, 'Display Hex character number per line')
        self.text_ctrl_line_nb = wx.TextCtrl(self, -1, str(val), size=(30, -1))
        sizer_display_line = wx.BoxSizer(wx.HORIZONTAL)
        sizer_display_line.Add(self.text_display, 1,
                               wx.ALIGN_CENTER_VERTICAL |
                               wx.EXPAND | wx.ALL, 2)
        sizer_display_line.Add(self.text_ctrl_line_nb, 0, wx.ALL, 2)

        sizer_line.Add(sizer_display_line, 0, wx.ALL, 2)
        v_sizer.Add(sizer_line, 0, wx.EXPAND | wx.ALL, 5)

        sizer_tree = wx.StaticBoxSizer(wx.StaticBox(self, -1), wx.VERTICAL)
        val = self.app.readKey(ai.CfgParaTreeMatchEnable)
        self.checkbox_tree_enabler = wx.CheckBox(
            self, -1, 'Enable tree selection with content highlighted in view')
        self.checkbox_tree_enabler.SetValue(val)
        sizer_tree.Add(self.checkbox_tree_enabler, 0, wx.ALL, 2)
        val = self.app.readKey(ai.CfgParaTreeAllMatches)
        self.checkbox_allmatch = wx.CheckBox(
            self, -1, 'Select all matches in the tree')
        self.checkbox_allmatch.SetValue(val)

        sizer_tree.Add(self.checkbox_allmatch, 0, wx.ALL, 2)
        v_sizer.Add(sizer_tree, 0, wx.EXPAND | wx.EAST | wx.WEST, 5)

        val = self.app.readKey(ai.CfgParaTreeMatchMode)
        self.radiobox_match = wx.RadioBox(self, -1, 'Match Algorithm',
                                          wx.DefaultPosition, wx.DefaultSize,
                                          ('Recursive Match', 'Precise Match'),
                                          1, wx.RA_SPECIFY_ROWS)
        self.radiobox_match.SetSelection(val)
        sizer_tree.Add(self.radiobox_match, 1, wx.EXPAND | wx.ALL)

        wx.EVT_CHECKBOX(self, self.checkbox_resizing.GetId(),
                        self.updateControls)
        wx.EVT_CHECKBOX(self, self.checkbox_tree_enabler.GetId(),
                        self.updateControls)

        self.updateControls()
        return v_sizer

    def updateControls(self, event=None):
        val = self.checkbox_resizing.IsChecked()
        self.text_display.Enable(not val)
        self.text_ctrl_line_nb.Enable(not val)

        val = self.checkbox_tree_enabler.IsChecked()
        self.checkbox_allmatch.Enable(val)
        self.radiobox_match.Enable(val)

    def validate(self):
        try:
            _ = int(self.text_ctrl_line_nb.GetValue())
        except ValueError:
            wx.MessageBox("Number for per line isn't correct, make sure "
                          "it isn't empty and is a valid integer",
                          'Error', wx.OK | wx.ICON_ERROR, self)
            return False

        return True

    def save(self):
        self.app.writeKey(ai.CfgParaEnableTextMode,
                          self.checkbox_text.IsChecked())
        self.app.writeKey(ai.CfgParaEnableAutoLine,
                          self.checkbox_resizing.IsChecked())
        self.app.writeKey(ai.CfgParaLineLength,
                          int(self.text_ctrl_line_nb.GetValue()))
        self.app.writeKey(ai.CfgParaTreeMatchEnable,
                          self.checkbox_tree_enabler.IsChecked())
        self.app.writeKey(ai.CfgParaTreeAllMatches,
                          self.checkbox_allmatch.IsChecked())
        self.app.writeKey(ai.CfgParaTreeMatchMode,
                          self.radiobox_match.GetSelection())


class ColorPage(PagePanel):
    def initControls(self):
        v_sizer = wx.BoxSizer(wx.VERTICAL)

        sizer_activities = wx.StaticBoxSizer(
            wx.StaticBox(self, -1, 'Activities Color'), wx.HORIZONTAL)

        h_sizer_activities = wx.FlexGridSizer(0, 4, 1, 0)
        h_sizer_activities.AddGrowableCol(1)
        h_sizer_activities.AddGrowableCol(3)

        h_sizer_activities.Add(wx.StaticText(self, -1, 'Font'), 0,
                               wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)

        rgb_val = utils.int2rgb(self.app.readKey(ai.CfgParaColorFontHigh))
        self.csel_font_color_activities = csel.ColourSelect(
            self, -1, '', rgb_val, size=(120, -1))
        h_sizer_activities.Add(self.csel_font_color_activities,
                               0, wx.EXPAND | wx.ALL)

        h_sizer_activities.Add(wx.StaticText(self, -1, 'Background'), 0,
                               wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)

        rgb_val = utils.int2rgb(self.app.readKey(ai.CfgParaColorBackHigh))
        self.csel_back_color_activities = csel.ColourSelect(
            self, -1, '', rgb_val, size=(120, -1))
        h_sizer_activities.Add(self.csel_back_color_activities,
                               0, wx.EXPAND | wx.ALL)

        sizer_activities.Add(h_sizer_activities, 1, wx.EXPAND | wx.ALL)
        v_sizer.Add(sizer_activities, 0, wx.EXPAND | wx.ALL, 5)

        sizer_bookmark = wx.StaticBoxSizer(
            wx.StaticBox(self, -1, 'Bookmark Color'), wx.HORIZONTAL)

        h_sizer_bookmark = wx.FlexGridSizer(0, 0, 4, 0)
        h_sizer_bookmark.AddGrowableCol(1)
        h_sizer_bookmark.AddGrowableCol(3)

        h_sizer_bookmark.Add(wx.StaticText(self, -1, 'Font'), 0,
                             wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)

        rgb_val = utils.int2rgb(self.app.readKey(ai.CfgParaColorFontLabel))
        self.csel_font_color_bookmark = csel.ColourSelect(
            self, -1, '', rgb_val, size=(120, -1))
        h_sizer_bookmark.Add(self.csel_font_color_bookmark,
                             1, wx.EXPAND | wx.ALL)

        h_sizer_bookmark.Add(wx.StaticText(self, -1, 'Bakground'), 0,
                             wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)

        rgb_val = utils.int2rgb(self.app.readKey(ai.CfgParaColorBackLabel))
        self.csel_back_color_bookmark = csel.ColourSelect(
            self, -1, '', rgb_val, size=(120, -1))
        h_sizer_bookmark.Add(self.csel_back_color_bookmark,
                             1, wx.EXPAND | wx.ALL)

        sizer_bookmark.Add(h_sizer_bookmark, 1, wx.EXPAND | wx.ALL)
        v_sizer.Add(sizer_bookmark, 0, wx.EXPAND | wx.ALL, 5)

        return v_sizer

    def save(self):
        int_val = utils.rgb2int(self.csel_font_color_activities.GetColour())
        self.app.writeKey(ai.CfgParaColorFontHigh, int_val)

        int_val = utils.rgb2int(self.csel_back_color_activities.GetColour())
        self.app.writeKey(ai.CfgParaColorBackHigh, int_val)

        int_val = utils.rgb2int(self.csel_font_color_bookmark.GetColour())
        self.app.writeKey(ai.CfgParaColorFontLabel, int_val)

        int_val = utils.rgb2int(self.csel_back_color_bookmark.GetColour())
        self.app.writeKey(ai.CfgParaColorBackLabel, int_val)


class PlxPage(PagePanel):
    def initControls(self):
        v_sizer = wx.BoxSizer(wx.VERTICAL)

        self.checkbox_auto_load = wx.CheckBox(self, -1,
                                              'Enable plug-in auto reloading')
        self.checkbox_auto_load.SetValue(
            self.app.readKey(ai.CfgParaReloadIndicator))
        v_sizer.Add(self.checkbox_auto_load, 0, wx.EXPAND | wx.ALL, 2)

        self.checkbox_load_background = wx.CheckBox(
            self, -1, 'Auto-load in background silently')
        self.checkbox_load_background.SetValue(
            self.app.readKey(ai.CfgParaReloadBackground))
        v_sizer.Add(self.checkbox_load_background, 0, wx.EXPAND | wx.ALL, 2)

        val = self.app.readKey(ai.CfgParaIgoreIntfError)
        self.checkbox_ignore_error = wx.CheckBox(
            self, -1, 'Ignore errors of extended plug-in interface invoking')
        self.checkbox_ignore_error.SetValue(val)
        v_sizer.Add(self.checkbox_ignore_error, 0, wx.EXPAND | wx.ALL, 2)

        val = self.app.readKey(ai.CfgParaIgnorePlxConfig)
        self.checkbox_ignore_config = wx.CheckBox(
            self, -1, 'Ignore configurations from the plug-ins')
        self.checkbox_ignore_config.SetValue(val)
        v_sizer.Add(self.checkbox_ignore_config, 0, wx.EXPAND | wx.ALL, 2)

        val = self.app.readKey(ai.CfgParaDisplayPicture)
        self.checkbox_display_pic = wx.CheckBox(
            self, -1, 'Display the picture if the object can be displayed')
        self.checkbox_display_pic.SetValue(val)
        v_sizer.Add(self.checkbox_display_pic, 0, wx.EXPAND | wx.ALL, 2)

        wx.EVT_CHECKBOX(self, self.checkbox_auto_load.GetId(),
                        self.updateControls)

        self.updateControls()
        return v_sizer

    def updateControls(self, event=None):
        self.checkbox_load_background.Enable(
            self.checkbox_auto_load.IsChecked())

    def save(self):
        self.app.writeKey(ai.CfgParaReloadIndicator,
                          self.checkbox_auto_load.IsChecked())
        self.app.writeKey(ai.CfgParaReloadBackground,
                          self.checkbox_load_background.IsChecked())
        self.app.writeKey(ai.CfgParaIgoreIntfError,
                          self.checkbox_ignore_error.IsChecked())
        self.app.writeKey(ai.CfgParaIgnorePlxConfig,
                          self.checkbox_ignore_config.IsChecked()),
        self.app.writeKey(ai.CfgParaDisplayPicture,
                          self.checkbox_display_pic.IsChecked())


class PlxDirsPage(PagePanel):
    def initControls(self):
        v_sizer = wx.FlexGridSizer(2, 0, 1, 0)
        v_sizer.AddGrowableCol(0)
        v_sizer.AddGrowableRow(0)

        self.list_dir = wx.ListCtrl(self, -1,
                                    style=wx.LC_REPORT | wx.LC_NO_HEADER)
        self.list_dir.InsertColumn(0, 'dir', wx.LIST_AUTOSIZE)
        self.list_dir.SetColumnWidth(0, 300)

        vals = self.app.readKey(ai.CfgParaExtraPlx)
        dirs = [e for e in vals.values() if e is not None and e != '']
        for k, val in enumerate(dirs):
            self.list_dir.InsertStringItem(k, val)

        v_sizer.Add(self.list_dir, 1, wx.EXPAND | wx.ALL, 5)

        h_sizer_buttons = wx.BoxSizer(wx.HORIZONTAL)
        self.button_add = wx.Button(self, -1, 'Add')
        h_sizer_buttons.Add(self.button_add)
        self.button_remove = wx.Button(self, -1, 'Remove')
        h_sizer_buttons.Add(self.button_remove)
        v_sizer.Add(h_sizer_buttons, 0, wx.ALL | wx.ALIGN_RIGHT, 5)

        wx.EVT_BUTTON(self, self.button_add.GetId(),
                      self.OnEventButtonAdd)
        wx.EVT_BUTTON(self, self.button_remove.GetId(),
                      self.OnEventButtonRemove)

        return v_sizer

    def OnEventButtonAdd(self, event):
        dialog = wx.DirDialog(self, "Choose a directory",
                              style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)

        if dialog.ShowModal() == wx.ID_OK:
            path = dialog.GetPath()
            for item in range(self.list_dir.GetItemCount()):
                if path == self.list_dir.GetItemText(item):
                    wx.MessageBox("Path '%s' is in the list" % path,
                                  'Error',
                                  wx.OK | wx.CENTRE | wx.ICON_ERROR)
                    break
            else:
                self.list_dir.InsertStringItem(0, path)

    def OnEventButtonRemove(self, event):
        item = self.list_dir.GetFirstSelected()
        while item != -1:
            self.list_dir.DeleteItem(item)
            item = self.list_dir.GetNextSelected(item)

    def save(self):
        dirs = list()
        for index in range(self.list_dir.GetItemCount()):
            dirs.append(self.list_dir.GetItemText(index))

        self.app.writeKey(ai.CfgParaExtraPlx, dirs)


class PlxEnablePage(PagePanel):
    def initControls(self):
        self.plx = self.app.readKey(ai.CfgParaPlxObject)

        self.cat = dict()
        self.updating = False

        v_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, ''), wx.VERTICAL)
        self.tree = ct.CustomTreeCtrl(self)
        v_sizer.Add(self.tree, 1, wx.ALL | wx.EXPAND, 5)

        self._initControls()

        self.tree.Bind(ct.EVT_TREE_ITEM_CHECKED, self.OnTreectrlChecked)

        return v_sizer

    def _initControls(self):
        enable_all = self.app.readKey(ai.CfgParaPlxEnableAll)
        plx = self.app.readKey(ai.CfgParaPlxObject)

        all_enabled = True
        self.tree.DeleteAllItems()
        self.root = self.tree.AddRoot('Plug-ins', ct_type=1)
        self.tree.SetItem3State(self.root, True)

        ci = plx.getCategory()
        cats = ci.getCategories() or list()
        cats.sort()
        for c in cats:
            plxs = plx.getPlxList(c, required_all=True)
            if len(plxs) == 0:
                continue

            enabled_plxs = (self.app.readKey(ai.CfgParaPlxEnabledList, c)
                            or '').split(',')
            self.cat[c] = self.tree.AppendItem(self.root,
                                               ci.getNameWithId(c),
                                               ct_type=1)
            self.tree.SetItem3State(self.cat[c], True)

            cat_enabled = True
            plx_enabled = self.app.readKey(ai.CfgParaPlxEnabledCategory, c)
            for p in plxs:
                item = self.tree.AppendItem(self.cat[c], p, ct_type=1)
                self.tree.SetItem3State(item, True)
                if p in enabled_plxs:
                    item.Set3StateValue(wx.CHK_CHECKED)
                elif enable_all or plx_enabled:
                    item.Set3StateValue(wx.CHK_UNDETERMINED)
                else:
                    cat_enabled = False

            if plx_enabled:
                self.tree.CheckItem(self.cat[c], wx.CHK_CHECKED)
            if enable_all or cat_enabled:
                self.tree.CheckItem(self.cat[c], wx.CHK_UNDETERMINED)
            else:
                all_enabled = False

        self.tree.ExpandAll()
        if enable_all:
            self.root.Set3StateValue(wx.CHK_CHECKED)
        else:
            val = [wx.CHK_UNCHECKED, wx.CHK_UNDETERMINED][all_enabled]
            self.root.Set3StateValue(val)

    def _updateControls(self, item):

        # convert 3-state to 2-state: customtreectrl use a 3-mod to turn
        # the value. here the new value converted:
        #
        # 3-state new value      | 3-state old value      | 2-state new
        # -----------------------+------------------------+------------------
        # wx.CHK_UNCHECKED (0)   | wx.CHK_UNDETERMINED (2)| wx.CHK_CHECKED
        # wx.CHK_CHECKED (1)     | wx.CHK_UNCHECKED (0)   | wx.CHK_CHECKED
        # wx.CHK_UNDETERMINED (2)| wx.CHK_CHECKED (1)     | wx.CHK_UNCHECKED

        def _m(value, table):
            return table[value]

        value = _m(item.Get3StateValue(),
                   [wx.CHK_CHECKED, wx.CHK_CHECKED, wx.CHK_UNCHECKED])
        item.Set3StateValue(value)

        all_enabled = True
        all_value = self.root.Get3StateValue()
        for c, v in self.cat.items():
            cat_enabled = True

            val = v.Get3StateValue()
            if val != wx.CHK_CHECKED:
                val = _m(all_value, [wx.CHK_UNCHECKED,
                         wx.CHK_UNDETERMINED, wx.CHK_UNCHECKED])

            # map the value from the category to the child
            #     Category           |    Child
            # -----------------------+--------------------
            # wx.CHK_UNCHECKED (0)   | wx.CHK_UNCHECKED
            # wx.CHK_CHECKED (1)     | wx.CHK_UNDETERMINED
            # wx.CHK_UNDETERMINED (2)| wx.CHK_UNDETERMINED
            va = _m(val, [wx.CHK_UNCHECKED,
                    wx.CHK_UNDETERMINED, wx.CHK_UNDETERMINED])

            (child, cookie) = self.tree.GetFirstChild(v)
            while child:
                # the child's value overwrite the category's
                if child.Get3StateValue() != wx.CHK_CHECKED:
                    self.tree.CheckItem2(child, va)

                if child.Get3StateValue() == wx.CHK_UNCHECKED:
                    cat_enabled = False

                (child, cookie) = self.tree.GetNextChild(v, cookie)

            # if one child isn't checked, set the category unchecked
            if v.Get3StateValue() != wx.CHK_CHECKED:
                if all_value == wx.CHK_CHECKED or cat_enabled:
                    self.tree.CheckItem2(v, wx.CHK_UNDETERMINED)
                else:
                    self.tree.CheckItem2(v, wx.CHK_UNCHECKED)

            if not cat_enabled:
                all_enabled = False

        if item is not self.root \
                and self.root.Get3StateValue() != wx.CHK_CHECKED:
            self.root.Set3StateValue(
                _m(all_enabled, [wx.CHK_UNCHECKED, wx.CHK_UNDETERMINED]))

        self.tree.Refresh()

    def OnTreectrlChecked(self, event):
        item = event.GetItem()
        self._updateControls(item)
        event.Skip()

    def save(self):
        for c in self.cat:
            plxs = list()

            (child, cookie) = self.tree.GetFirstChild(self.cat[c])
            while child:
                if child.Get3StateValue() == 1:
                    plxs.append(child.GetText())

                child, cookie = self.tree.GetNextChild(self.cat[c], cookie)

            if len(plxs) > 0:
                self.app.writeKey(ai.CfgParaPlxEnabledList,
                                  ','.join(plxs), c)
            else:
                self.app.removeKey(ai.CfgParaPlxEnabledList, c)

            if self.cat[c].Get3StateValue() == 1:
                self.app.writeKey(ai.CfgParaPlxEnabledCategory, True, c)
            else:
                self.app.removeKey(ai.CfgParaPlxEnabledCategory, c)

        self.app.writeKey(ai.CfgParaPlxEnableAll,
                          [0, 1, 0][self.root.Get3StateValue()])


class PlxConfigPage(PagePanel):
    def initControls(self):
        from ab_config import RUNTIME_PLUGIN_SECTION, TemplateHandler
        from ab_propgrid import PropertyGrid

        # the plug-in config may not be registered in the core config. this
        # fake handler will handle both cases from the core config and the one
        # built in the class. fixChar need set to True like
        # Config.installPlxConfig
        class _TemplateHandler(TemplateHandler):
            def __init__(self, app, template):
                TemplateHandler.__init__(self, template, fixChar=True)

                self.app = app
                # update the values if it's in the core config
                for item in self.items.values():
                    val = self.app.readKey(item.name)
                    if val is not None:
                        item.value = val

            def readKey(self, key, idx=None, escape=True):
                ret = self.app.readKey(key, idx, escape)
                if ret is None:
                    item = self.getItem(key)
                    if item is not None:
                        ret = self.get(key, idx, escape)

                return ret

        plx = self.app.readKey(ai.CfgParaPlxObject)
        # build the template
        self.template = dict()
        for name, opts in plx.getConfigs().items():
            options = dict()
            for o in opts or dict():
                options[o.get('name', '_')] = o

            sname = name
            elem = plx.getElement(name)
            if elem is not None:
                sname = elem.getShortname()

            self.template[name] = {
                'name': name,
                'section': '%s/%s' % (RUNTIME_PLUGIN_SECTION, sname),
                'options': options
            }

        self.reader = _TemplateHandler(self.app, self.template)
        self.prop = PropertyGrid(self.app, self, self.template, self.reader,
                                 useOptionName=True)
        sizer = wx.BoxSizer()
        sizer.Add(self.prop, 1, wx.EXPAND, 0)

        return sizer

    def isValid(self):
        plx = self.app.readKey(ai.CfgParaPlxObject)
        # wxPython v2.8 or later support PropertyGrid
        return wx.VERSION[:2] > (2, 8) and len(plx.getConfigs().keys()) > 0

    def save(self):
        vals = self.prop.getValues()
        for ii, item in self.reader.getItems().items():
            self.app.writeKey(item.name, vals[ii])


class TreePage(PagePanel):
    def initControls(self):
        self.dispfmt = {
            'Standard': {'key': ai.CfgParaTreeDispFormat},
            'Simple': {'key': ai.CfgParaTreeSimpleDispFormat},
            'Simplest': {'key': ai.CfgParaTreeSimplestDispFormat}
        }

        for fmt in self.dispfmt.values():
            fmt['val'] = self.app.readKey(fmt['key'])

        v_sizer = wx.BoxSizer(wx.VERTICAL)

        sizer_format = wx.StaticBoxSizer(wx.StaticBox(self, -1,
                                         'Tree Context Format'),
                                         wx.VERTICAL)

        val = self.app.readKey(ai.CfgParaTreeUseSimpleAsDefault)
        self.checkbox_simple = wx.CheckBox(
            self, -1, 'Use Simple as the default tree format')
        self.checkbox_simple.SetValue(val)
        sizer_format.Add(self.checkbox_simple, 1, wx.ALL)

        h_sizer_format = wx.FlexGridSizer(0, 0, 2, 0)
        h_sizer_format.AddGrowableCol(1)

        self.combo_box_format = wx.ComboBox(
            self, -1, 'Standard', style=wx.CB_READONLY,
            choices=self.dispfmt.keys())

        h_sizer_format.Add(self.combo_box_format, 1, wx.ALL)
        self.text_ctrl_format = wx.TextCtrl(self, -1)
        h_sizer_format.Add(self.text_ctrl_format, 1, wx.EXPAND | wx.ALL)
        sizer_format.Add(h_sizer_format, 1, wx.EXPAND | wx.ALL)
        sizer_format.Add(
            wx.StaticText(self, -1,
                          '%c: content, %s: subject, %o: offset, %l: size'),
            0, wx.ALL, 1)
        v_sizer.Add(sizer_format, 0, wx.EXPAND | wx.EAST | wx.WEST, 5)

        h_sizer_tree = wx.BoxSizer(wx.HORIZONTAL)
        h_sizer_tree.Add(wx.StaticText(
            self, -1, 'Ignore the contents whose level is more than'))

        val = self.app.readKey(ai.CfgParaTreeIgnoredLevel)
        self.text_ctrl_tree_level = wx.TextCtrl(self, -1, str(val))
        h_sizer_tree.Add(self.text_ctrl_tree_level, 1, wx.ALL, 1)

        v_sizer.Add(h_sizer_tree, 0, wx.EXPAND | wx.ALL, 5)

        self.updateControls()

        self.combo_box_format.Bind(wx.EVT_COMBOBOX, self.updateControls)
        self.text_ctrl_format.Bind(wx.EVT_TEXT, self.updateTextFormat)

        return v_sizer

    def updateControls(self, event=None):
        disp = self.combo_box_format.GetValue()
        self.text_ctrl_format.SetValue(self.dispfmt[disp]['val'])

    def updateTextFormat(self, event):
        disp = self.combo_box_format.GetValue()
        self.dispfmt[disp]['val'] = event.GetString()

    def validate(self):
        str_level = self.text_ctrl_tree_level.GetValue().strip()

        try:
            int_level = int(str_level)
        except ValueError:
            int_level = -1
        finally:
            if int_level <= 4:
                wx.MessageBox('level should be an integer more than 4')
                return False

        for k, fmt in self.dispfmt.items():
            if fmt['val'].strip() == '':
                wx.MessageBox('The content for "%s" is empty' % k, 'Error',
                              wx.OK | wx.ICON_ERROR, self)
                return False

        return True

    def save(self):
        self.app.writeKey(ai.CfgParaTreeUseSimpleAsDefault,
                          self.checkbox_simple.GetValue())
        self.app.writeKey(ai.CfgParaTreeIgnoredLevel,
                          int(self.text_ctrl_tree_level.GetValue()))

        for fmt in self.dispfmt.values():
            self.app.writeKey(fmt['key'], fmt['val'].rstrip())


class DebugPage(PagePanel):
    def initControls(self):
        v_sizer = wx.BoxSizer(wx.VERTICAL)
        val = self.app.readKey(ai.CfgParaDeveloperMethod)
        self.checkbox_developer_method = wx.CheckBox(
            self, -1, 'Enable the developer method to show more items')
        self.checkbox_developer_method.SetValue(val)
        v_sizer.Add(self.checkbox_developer_method, 0, wx.EXPAND | wx.ALL, 5)

        sizer_verbose = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Verbose'),
                                          wx.VERTICAL)
        hsizer_level = wx.BoxSizer(wx.HORIZONTAL)
        text_verbose_level = wx.StaticText(self, -1, 'Level')
        hsizer_level.Add(text_verbose_level)

        list_verbose = ('0 - CRITIC', '1 - ERROR', '2 - WARNING',
                        '3 - INFOMATION', '4 - DEBUG')
        val = min(self.app.readKey(ai.CfgParaVerbose), len(list_verbose) - 1)
        self.combobox_verbose = wx.ComboBox(self, -1, style=wx.CB_READONLY,
                                            choices=list_verbose)
        self.combobox_verbose.SetValue(list_verbose[val])
        hsizer_level.Add(self.combobox_verbose, 1, wx.EXPAND | wx.ALL)

        sizer_verbose.Add(hsizer_level, 1, wx.EXPAND | wx.ALL)
        v_sizer.Add(sizer_verbose, 0, wx.EXPAND | wx.ALL, 5)

        sizer_logfile = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Log File'),
                                          wx.VERTICAL)
        hsizer_logfile = wx.BoxSizer(wx.HORIZONTAL)
        self.logfile = self.app.readKey(ai.CfgParaFileLogName, escape=False)
        self.text_ctrl_logfile = wx.TextCtrl(self, -1, self.logfile)
        self.text_ctrl_logfile.Enable(False)
        hsizer_logfile.Add(self.text_ctrl_logfile, 1, wx.EXPAND | wx.ALL)

        self.button_file = wx.Button(self, -1, '...', size=(30, -1))
        hsizer_logfile.Add(self.button_file)
        sizer_logfile.Add(hsizer_logfile, 1, wx.EXPAND | wx.ALL)
        v_sizer.Add(sizer_logfile, 0, wx.EXPAND | wx.ALL, 5)

        wx.EVT_BUTTON(self, self.button_file.GetId(), self.OnEventButtonFile)
        return v_sizer

    def save(self):
        self.app.writeKey(ai.CfgParaDeveloperMethod,
                          self.checkbox_developer_method.IsChecked())
        self.app.writeKey(ai.CfgParaVerbose,
                          self.combobox_verbose.GetSelection())
        # writ the log level immediately
        logo = self.app.readKey(ai.CfgParaLogObject)
        logo.setlevel(int(self.combobox_verbose.GetSelection()))

        val = self.text_ctrl_logfile.GetValue()
        self.app.writeKey(ai.CfgParaFileLogName,
                          val, escape=(val != self.logfile))

    def OnEventButtonFile(self, event):
        dialog = wx.FileDialog(
            self, message="Choose a logfile",
            defaultDir=os.getcwd(),
            wildcard='Log Files (*.log, *.txt)|*.log;*.txt',
            style=wx.SAVE | wx.CHANGE_DIR)

        if dialog.ShowModal() == wx.ID_OK:
            filename = self.app.readKey(ai.CfgParaPlatform).getFileName(
                (dialog.GetPaths())[0])

            self.text_ctrl_logfile.SetValue(filename)
